/*
 * commons is a based project implemented
 * Copyright (C) 2024 Jasmine
 */
package com.easy.core.jackson;

import java.time.temporal.Temporal;
import java.util.Collection;

import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;

import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.deser.DefaultDeserializationContext;
import com.fasterxml.jackson.databind.ser.DefaultSerializerProvider;
import com.fasterxml.jackson.databind.ser.SerializerFactory;

/**
 * 该类的作用是使用SpringBoot自动配置的Jackson ObjectMapper支持将null的数组转为[]、null字符串转为""
 *
 * <p>
 * 1.发现{@link com.fasterxml.jackson.databind.SerializerProvider#findNullValueSerializer(BeanProperty)}用于序列化属性值为null；
 * 而该接口的默认实现是通过{@link com.fasterxml.jackson.databind.SerializerProvider#getDefaultNullValueSerializer()}获取的
 * {@link com.fasterxml.jackson.databind.SerializerProvider#_nullValueSerializer};
 * 默认初始为{@link com.fasterxml.jackson.databind.ser.std.NullSerializer#instance}
 * 2.因为{@link com.fasterxml.jackson.databind.SerializerProvider}为抽象类；
 * -->{@link com.fasterxml.jackson.databind.ser.DefaultSerializerProvider}也为抽象类，并且所有自定义的{@link com.fasterxml.jackson.databind.SerializerProvider}都必须继承此类；因为{@link com.fasterxml.jackson.databind.ObjectMapper}需要这个类型
 * -->默认实现类{@link Impl},该类在
 * {@link com.fasterxml.jackson.databind.ObjectMapper#ObjectMapper(JsonFactory, DefaultSerializerProvider, DefaultDeserializationContext)}的
 * 构造函数中有如下一行代码： <code>
 * _serializerProvider = (sp == null) ? new DefaultSerializerProvider.Impl() : sp;
 * </code> 我们可以看到，在没有指定DefaultSerializerProvider的时候，默认实例化DefaultSerializerProvider.Impl() <br>
 * 3.下面我们就来理一下SpringBoot是怎么实例化ObjectMapper
 * 首先定位{@link org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration}配置类 -->在
 * {@link org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration.JacksonObjectMapperBuilderConfiguration#jacksonObjectMapperBuilder(List)}方法上发现了
 * <code>
 * @ConditionalOnMissingBean(Jackson2ObjectMapperBuilder.class) </code>
 * 也就是在没有Jackson2ObjectMapperBuilder时会执行jacksonObjectMapperBuilder(List)方法； 而JacksonAutoConfiguration配置类中的其他内容都是
 * <code>
 * @ConditionalOnClass({ ObjectMapper.class, Jackson2ObjectMapperBuilder.class })
 * </code> 需要依托Jackson2ObjectMapperBuilder和ObjectMapper
 * 4.通过第3步我们定位到Jackson2ObjectMapperBuilder实在JacksonAutoConfiguration
 * .JacksonObjectMapperBuilderConfiguration#jacksonObjectMapperBuilder(List)中实例的，那么ObjectMapper呢？ -->在
 * {@link org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration.JacksonObjectMapperConfiguration#jacksonObjectMapper(Jackson2ObjectMapperBuilder)}中实例化的
 * 该方法被
 * <code>
 * @ConditionalOnMissingBean(ObjectMapper.class) </code>
 * 注解修饰，并且传递Jackson2ObjectMapperBuilder参数，通过{@link org.springframework.http.converter.json.Jackson2ObjectMapperBuilder#build()}构造ObjectMapper
 * -->进而调整用{@link com.fasterxml.jackson.databind.ObjectMapper#ObjectMapper()}
 * -->{@link com.fasterxml.jackson.databind.ObjectMapper#ObjectMapper(JsonFactory, DefaultSerializerProvider, DefaultDeserializationContext)}
 * 在此处就对接到了第2步中
 *
 * <pre>
 * _serializerProvider = (sp == null) ? new DefaultSerializerProvider.Impl() : sp;
 * </pre>
 * 
 * 总结： 所以本类通过继承{@link com.fasterxml.jackson.databind.ser.DefaultSerializerProvider}实现自定义的null处理转换逻辑
 * </p>
 *
 * @author Jasmine
 * @since 1.0.0
 */
public class NullValueSerializerProvider extends DefaultSerializerProvider {

    private static final long serialVersionUID = 7319093081185194874L;

    public NullValueSerializerProvider() {
        super();
    }

    public NullValueSerializerProvider(NullValueSerializerProvider src) {
        super(src);
    }

    protected NullValueSerializerProvider(SerializerProvider src, SerializationConfig config, SerializerFactory f) {
        super(src, config, f);
    }

    @Override
    public DefaultSerializerProvider createInstance(SerializationConfig config, SerializerFactory jsf) {
        return new NullValueSerializerProvider(this, config, jsf);
    }

    @Override
    public DefaultSerializerProvider copy() {
        if (getClass() != NullValueSerializerProvider.class) {
            return super.copy();
        }
        return new NullValueSerializerProvider(this);
    }

    @Override
    public JsonSerializer<Object> findNullValueSerializer(BeanProperty property) throws JsonMappingException {
        if (isStringType(property)) {
            return NullJsonComponent.NULL_STRING_SERIALIZER;
        } else if (isNumberType(property)) {
            return NullJsonComponent.NULL_NUMBER_SERIALIZER;
        } else if (isArrayType(property)) {
            return NullJsonComponent.NULL_ARRAY_SERIALIZER;
        } else if (isBooleanType(property)) {
            return NullJsonComponent.NULL_BOOLEAN_SERIALIZER;
        } else {
            // return NullJsonComponent.NULL_OBJECT_SERIALIZER;
            return super.findNullValueSerializer(property);
        }
    }

    /**
     * 是否是String
     */
    private boolean isStringType(BeanProperty property) {
        Class<?> clazz = property.getType().getRawClass();
        return CharSequence.class.isAssignableFrom(clazz) || Character.class.isAssignableFrom(clazz);
    }

    /**
     * 是否是数值类型
     */
    private boolean isNumberType(BeanProperty writer) {
        Class<?> clazz = writer.getType().getRawClass();
        return Number.class.isAssignableFrom(clazz);
    }

    /**
     * 是否是数组
     */
    private boolean isArrayType(BeanProperty property) {
        Class<?> clazz = property.getType().getRawClass();
        return clazz.isArray() || Collection.class.isAssignableFrom(clazz);
    }

    /**
     * 是否是boolean
     */
    private boolean isBooleanType(BeanProperty writer) {
        Class<?> clazz = writer.getType().getRawClass();
        return clazz.equals(Boolean.class);
    }

    /**
     * 是否是时间
     */
    private boolean isTemporalType(BeanProperty writer) {
        Class<?> clazz = writer.getType().getRawClass();
        return Temporal.class.isAssignableFrom(clazz);
    }
}